import { toHex4, toHex8 } from "./hex";
import EJS from 'ejs';

export const PORTS = [
    'GPIOA',
    'GPIOB',
    'GPIOC',
    'GPIOD',
    'GPIOE',
    'GPIOF',
    'GPIOG',
    'GPIOH',
];

export const PINS = [
    0, 1, 2, 3,
    4, 5, 6, 7,
    8, 9, 10, 11,
    12, 13, 14, 15,
]

export const MODES = [
    '输入模式',
    '输出模式, 最大速度10MHz',
    '输出模式, 最大速度2MHz',
    '输出模式, 最大速度50MHz',
];

export const OUTS = [
    "默认",
    "低电平",
    "高电平",
];

export const CONFS_INPUT = [
    '模拟输出模式',
    '浮空输入模式',
    '上拉/下拉输入模式',
];

export const PUPDS = [
    '上拉输入模式',
    '下拉输入模式',
];

export const CONFS_OUTPUT = [
    '通用推挽输出模式',
    '通用开漏输出模式',
    '复用功能推挽输出模式',
    '服用功能开漏输出模式',
];

export interface GpioPinCfg {
    mode: string;
    conf_input: string;
    conf_output: string;
    pupd: string;
    out: string;

    port: string;
    pin: number;
    name: string;

    id: symbol;
}

export function defaultCfg(): GpioPinCfg {
    return {
        mode: MODES[0],
        conf_input: CONFS_INPUT[0],
        conf_output: CONFS_OUTPUT[0],
        pupd: PUPDS[0],
        out: OUTS[0],
        port: PORTS[0],
        pin: PINS[0],
        name: '',
        id: Symbol(),
    };
}

function calc_cr(cfg: GpioPinCfg) {
    const mode = MODES.indexOf(cfg.mode);
    if (cfg.mode == MODES[0]) {
        return (CONFS_INPUT.indexOf(cfg.conf_input) << 2) | mode;
    }
    return (CONFS_OUTPUT.indexOf(cfg.conf_output) << 2) | mode;
}

function calc_bsrr(cfg: GpioPinCfg) {
    if (cfg.mode == MODES[0]) {
        if (cfg.conf_input == CONFS_INPUT[2]) {
            if (cfg.pupd == PUPDS[0]) {
                return 1;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    } else {
        if ((cfg.conf_output == CONFS_OUTPUT[0]) || (cfg.conf_output == CONFS_OUTPUT[1])) {
            if (cfg.out == OUTS[2]) {
                return 1;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }
}

function calc_brr(cfg: GpioPinCfg) {
    if (cfg.mode == MODES[0]) {
        if (cfg.conf_input == CONFS_INPUT[2]) {
            if (cfg.pupd == PUPDS[1]) {
                return 1;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    } else {
        if ((cfg.conf_output == CONFS_OUTPUT[0]) || (cfg.conf_output == CONFS_OUTPUT[1])) {
            if (cfg.out == OUTS[1]) {
                return 1;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }
}

interface RegState {
    crl_mask: number;
    crh_mask: number;
    crl: number;
    crh: number;
    brr: number;
    bsrr: number;
}

interface RegStateStr {
    crl_mask: string;
    crh_mask: string;
    crl: string;
    crh: string;
    brr: string;
    bsrr: string;
}

interface Reg {
    port: string;
    state: RegState;
    str: RegStateStr;
}

function toStr(reg: RegState): RegStateStr {
    return {
        crl_mask: `0x${toHex8(reg.crl_mask)}`,
        crh_mask: `0x${toHex8(reg.crh_mask)}`,
        crl: `0x${toHex8(reg.crl)}`,
        crh: `0x${toHex8(reg.crh)}`,
        brr: `0x${toHex4(reg.brr)}`,
        bsrr: `0x${toHex4(reg.bsrr)}`,
    };
}

export function calc(cfgs: GpioPinCfg[]) {
    const map = new Map<string, GpioPinCfg[]>();
    for (const cfg of cfgs) {
        let arr = map.get(cfg.port);
        if (arr == null) {
            arr = [];
            map.set(cfg.port, arr);
        }
        arr.push(cfg);
    }
    const groups: Reg[] = [];
    for (const port of map.keys()) {
        const pins = map.get(port);
        if (pins == null) {
            continue;
        }
        const crh_mask = pins.filter((item) => item.pin >= 8)
            .reduce((s, item) => {
                const p = item.pin - 8;
                return s | (0x0F << (4 * p));
            }, 0);
        const crl_mask = pins.filter((item) => item.pin < 8)
            .reduce((s, item) => {
                const p = item.pin;
                return s | (0x0F << (4 * p));
            }, 0);
        const crh = pins.filter((item) => item.pin >= 8)
            .reduce((s, item) => {
                const p = item.pin - 8;
                return s | (calc_cr(item) << (4 * p));
            }, 0);
        const crl = pins.filter((item) => item.pin < 8)
            .reduce((s, item) => {
                const p = item.pin;
                return s | (calc_cr(item) << (4 * p));
            }, 0);
        const brr = pins.reduce((s, item) => {
            return s | ( calc_brr(item) << item.pin );
        }, 0);
        const bsrr = pins.reduce((s, item) => {
            return s | ( calc_bsrr(item) << item.pin );
        }, 0);
        const state: RegState = {
            crl_mask,
            crh_mask,
            crl,
            crh,
            brr,
            bsrr,
        };
        groups.push({
            port: port,
            state,
            str: toStr(state),
        });
    }
    const s = EJS.render(gpioInitSrc, {
        items: groups
    });
    return (removeEmptyLines(s));
}

function removeEmptyLines(input: string): string {
    // 使用正则表达式匹配空行（包括只有空格的行）
    // \s 匹配任何空白字符，包括空格、制表符、换页符等等
    // * 表示匹配前面的子表达式零次或多次
    // ^ 表示匹配输入字符串的开始位置
    // $ 表示匹配输入字符串的结束位置
    // gm 是修饰符：g 表示全局匹配，m 表示多行匹配
    return input.replace(/^\s*[\r\n]/gm, '');
  }

const gpioInitSrc = `
<% for (const item of items) { %>
<% if (item.state.crl_mask != 0) { %>
<% if (item.state.crl != 0) { %>
<%= item.port %>->CRL = <%= item.port %>->CRL & (~<%= item.str.crl_mask %>) | <%= item.str.crl %>;
<% } else { %>
<%= item.port %>->CRL = <%= item.port %>->CRL & (~<%= item.str.crl_mask %>);
<% } %>
<% } %>
<% if (item.state.crh_mask != 0) { %>
<% if (item.state.crh != 0) { %>
<%= item.port %>->CRH = <%= item.port %>->CRH & (~<%= item.str.crh_mask %>) | <%= item.str.crh %>;
<% } else { %>
<%= item.port %>->CRH = <%= item.port %>->CRH & (~<%= item.str.crh_mask %>);
<% } %>
<% } %>
<% if (item.state.brr != 0) { %>
<%= item.port %>->BRR = <%= item.str.brr %>;
<% } %>
<% if (item.state.bsrr != 0) { %>
<%= item.port %>->BSRR = <%= item.str.bsrr %>;
<% } %>
<% } %>
`;